package cn.jiedanba.itext5.check;

import org.apache.commons.io.IOUtils;
import org.bouncycastle.asn1.*;
import org.bouncycastle.asn1.x509.AccessDescription;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
import org.bouncycastle.cert.ocsp.*;
import org.bouncycastle.operator.DigestCalculatorProvider;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.cert.X509Certificate;

/**
 * 使用OCSP查询证书状态
 */
public class OCSPCheck {

    private X509Certificate cert;

    public OCSPCheck() {

    }

    public OCSPCheck(X509Certificate cert) {
        this.cert = cert;
    }

    /**
     * 检查证书是否有效
     *
     * @return true：有效 false：无效
     */
    public boolean checkCertStatus() {
        try {
            String ocspUrl = getOcspUrl(cert);

            if (ocspUrl == null) {
                throw new IllegalArgumentException("证书无OCSP查询地址");
            }
            DigestCalculatorProvider digCalcProv = new JcaDigestCalculatorProviderBuilder().build();
            CertificateID certId = new CertificateID(digCalcProv.get(CertificateID.HASH_SHA1),
                    new JcaX509CertificateHolder(cert), cert.getSerialNumber());
            OCSPReqBuilder ocspReqBuilder = new OCSPReqBuilder();
            ocspReqBuilder.addRequest(certId);

            // 生成OCSP请求
            OCSPReq ocspReq = ocspReqBuilder.build();

            byte[] reqByte = ocspReq.getEncoded();
            URL url = new URL(ocspUrl);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("POST");
            connection.setRequestProperty("Content-Type", "application/ocsp-request");
            connection.setRequestProperty("Accept", "application/ocsp-response");
            connection.setDoOutput(true);
            connection.setConnectTimeout(5000);
            connection.setReadTimeout(5000);
            connection.setDoOutput(true);
            OutputStream out = connection.getOutputStream();
            DataOutputStream dataOut = new DataOutputStream(new BufferedOutputStream(out));
            dataOut.write(reqByte);
            dataOut.flush();
            dataOut.close();
            InputStream in = (InputStream) connection.getContent();
            // 获取到结果
            byte[] response = IOUtils.toByteArray(in);

            // 解码OCSP响应
            OCSPResp ocspResp = new OCSPResp(response);
            BasicOCSPResp basicResponse = (BasicOCSPResp) ocspResp.getResponseObject();

            CertificateStatus certStatus = basicResponse.getResponses()[0].getCertStatus();
            if (certStatus == CertificateStatus.GOOD) {
                return true;
            } else {
                return false;
            }
        } catch (Exception e) {
            throw new RuntimeException("OCSP检查出错", e);
        }
    }

    /**
     * 从证书中获取OCSP URL
     *
     * @param certificate
     * @return
     * @throws IOException
     */
    private String getOcspUrl(X509Certificate certificate) throws IOException {
        try {
            // 从证书的扩展字段中获取OCSP URL
            byte[] extensionValue = certificate.getExtensionValue(Extension.authorityInfoAccess.getId());
            if (extensionValue == null) {
                return null;
            }
            String ocspUrl = null;
            ASN1InputStream asn1InputStream = null;
            try {
                // 解码扩展字段
                asn1InputStream = new ASN1InputStream(extensionValue);
                ASN1OctetString octetString = (ASN1OctetString) asn1InputStream.readObject();

                // 解码扩展字段的内容
                ASN1InputStream extValueInputStream = new ASN1InputStream(octetString.getOctets());
                ASN1Sequence extSequence = (ASN1Sequence) extValueInputStream.readObject();

                // 查找OCSP URL
                for (int i = 0; i < extSequence.size(); i++) {
                    ASN1Sequence seq = (ASN1Sequence) extSequence.getObjectAt(i);
                    ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier) seq.getObjectAt(0);
                    if (oid.getId().equals("1.3.6.1.5.5.7.48.1")) {
                        AccessDescription accessDescription = AccessDescription.getInstance(extSequence.getObjectAt(0));
                        if (accessDescription.getAccessLocation().getTagNo() == GeneralName.uniformResourceIdentifier) {
                            ocspUrl = DERIA5String.getInstance(accessDescription.getAccessLocation().getName())
                                    .getString();
                            break;
                        }
                    }
                }

            } finally {
                if (asn1InputStream != null) {
                    asn1InputStream.close();
                }
            }
            return ocspUrl;
        } catch (Exception e) {
            throw new RuntimeException("从证书中获取OCSP URL出错", e);
        }

    }
}
